using System;
using ch.ethz.ssh2;
using ch.ethz.ssh2.crypto.digest;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
namespace ch.ethz.ssh2.crypto.dh
{
	
	/// <summary> DhGroupExchange.
	/// 
	/// </summary>
	/// <author>  Christian Plattner
	/// </author>
	/// <version>  2.50, 03/15/10
	/// </version>
	public class DhGroupExchange
	{
		/// <returns> Returns the e.
		/// </returns>
		virtual public BigInteger E
		{
			get
			{
				if (e == null)
					throw new System.SystemException("Not initialized!");
				
				return e;
			}
			
		}
		/// <returns> Returns the shared secret k.
		/// </returns>
		virtual public BigInteger K
		{
			get
			{
				if (k == null)
					throw new System.SystemException("Shared secret not yet known, need f first!");
				
				return k;
			}
			
		}
		/// <summary> Sets f and calculates the shared secret.</summary>
		virtual public BigInteger F
		{
			set
			{
				if (e == null)
					throw new System.SystemException("Not initialized!");
				
				BigInteger zero = BigInteger.ValueOf(0);
				
				if (zero.CompareTo((System.Object) value) >= 0 || p.CompareTo((System.Object) value) <= 0)
					throw new System.ArgumentException("Invalid f specified!");

                this.f = value;
                this.k = f.ModPow(x, p);
			}
			
		}
		/* Given by the standard */
		

		private BigInteger p;

		private BigInteger g;
		
		/* Client public and private */
		

		private BigInteger e;

		private BigInteger x;
		
		/* Server public */
		
		
		private BigInteger f;
		
		/* Shared secret */
		
		
		private BigInteger k;
		
		
		public DhGroupExchange(BigInteger p, BigInteger g)
		{
			this.p = p;
			this.g = g;
		}
		
		public virtual void  init(SecureRandom rnd)
		{
			k = null;
			
			x = new BigInteger(p.BitLength - 1, rnd);
            e = g.ModPow(x, p);
		}
		
		public virtual byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload, byte[] serverKexPayload, byte[] hostKey, DHGexParameters para)
		{
			HashForSSH2Types hash = new HashForSSH2Types("SHA1");
			
			hash.updateByteString(clientversion);
			hash.updateByteString(serverversion);
			hash.updateByteString(clientKexPayload);
			hash.updateByteString(serverKexPayload);
			hash.updateByteString(hostKey);
			if (para.Min_group_len > 0)
				hash.updateUINT32(para.Min_group_len);
			hash.updateUINT32(para.Pref_group_len);
			if (para.Max_group_len > 0)
				hash.updateUINT32(para.Max_group_len);
			hash.updateBigInt(p);
			hash.updateBigInt(g);
			hash.updateBigInt(e);
			hash.updateBigInt(f);
			hash.updateBigInt(k);
			
			return hash.getDigest();
		}
	}
}